<?php
namespace app\api\logic;


class Wxpay
{
    const appid      ="wx41d90754cf48389f";
    const mch_id     ="1550646201";//商户号
//    const key        ="542b3a9e6b24bdec71a8f2404fba7f4e";//支付秘钥
    const key        ="d41d8cd98f00b204e9800998ecf8427e";//签名key
    const trade_type = "APP";
    const notify_url = "https://www.ttyjf.cn/api/index/wxpay";

    //建立请求
    static public function http_post($url='',$post_data=array(),$header=array(),$timeout=30) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 跳过证书检查
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 从证书中检查SSL加密算法是否存在
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);

        $response = curl_exec($ch);
        curl_close($ch);
        return $response;
    }
    //获取客户端ip
    static public function get_client_ip($type = 0)
    {
        $type = $type ? 1 : 0;
        static $ip = NULL;
        if ($ip !== NULL) return $ip[$type];
        if (isset($_SERVER['HTTP_X_REAL_IP'])) {//nginx 代理模式下，获取客户端真实IP
            $ip = $_SERVER['HTTP_X_REAL_IP'];
        } elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {//客户端的ip
            $ip = $_SERVER['HTTP_CLIENT_IP'];
        } elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {//浏览当前页面的用户计算机的网关
            $arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
            $pos = array_search('unknown', $arr);
            if (false !== $pos) unset($arr[$pos]);
            $ip = trim($arr[0]);
        } elseif (isset($_SERVER['REMOTE_ADDR'])) {
            $ip = $_SERVER['REMOTE_ADDR'];//浏览当前页面的用户计算机的ip地址
        } else {
            $ip = $_SERVER['REMOTE_ADDR'];
        }
        // IP地址合法验证
        $long = sprintf("%u", ip2long($ip));
        $ip = $long ? array($ip, $long) : array('0.0.0.0', 0);
        return $ip[$type];
    }

    //生成随机数并返回
    static public function getNonceStr()
    {
        $code = "";
        for ($i = 0; $i > 10; $i++) {
            $code .= mt_rand(1000);        //获取随机数
        }
        $nonceStrTemp = md5($code);
//        var_dump($nonceStrTemp);die;
        $nonce_str = mb_substr($nonceStrTemp, 5, 37);      //MD5加密后截取32位字符
//        var_dump($nonce_str);die;
        return $nonce_str;
    }
    /**
     * 获取参数签名；
     * @param  Array  要传递的参数数组
     * @return String 通过计算得到的签名；
     */
    static private function getSign($params)
    {
//        var_dump($params);die;
        ksort($params);        //将参数数组按照参数名ASCII码从小到大排序
        foreach ($params as $key => $item) {
            if (!empty($item)) {         //剔除参数值为空的参数
                $newArr[] = $key . '=' . $item;     // 整合新的参数数组
            }
        }
        $stringA = implode("&", $newArr);         //使用 & 符号连接参数
        $stringSignTemp = $stringA . "&key=" . self::key;        //拼接key
        $stringSignTemp = MD5($stringSignTemp);       //将字符串进行MD5加密
        $sign = strtoupper($stringSignTemp);      //将所有字符转换为大写
//        var_dump($sign);die;
        return $sign;

    }

    //为微信官方返回的数据类型做准备
    static public function arrToString($params)
    {
//        var_dump($params);die;
        ksort($params);        //将参数数组按照参数名ASCII码从小到大排序

        foreach ($params as $key => $item) {
            if (!empty($item)) {         //剔除参数值为空的参数
                $newArr[] = $key . '=' . $item;     // 整合新的参数数组
            }
        }
        $stringA = implode("&", $newArr);

        return $stringA;

    }

    /**
     * 拼装请求的数据
     * @return  String 拼装完成的数据
     */
    static public function setSendData($data=array())
    {
//        var_dump($data);die;
        $sTpl = "<xml>
                        <appid><![CDATA[%s]]></appid>
                        <body><![CDATA[%s]]></body>
                        <mch_id><![CDATA[%s]]></mch_id>
                        <nonce_str><![CDATA[%s]]></nonce_str>
                        <notify_url><![CDATA[%s]]></notify_url>
                        <out_trade_no><![CDATA[%s]]></out_trade_no>
                        <spbill_create_ip><![CDATA[%s]]></spbill_create_ip>
                        <total_fee><![CDATA[%d]]></total_fee>
                        <trade_type><![CDATA[%s]]></trade_type>
                        <sign><![CDATA[%s]]></sign>
                    </xml>";                          //xml数据模板

        $nonce_str = self::getNonceStr();        //调用随机字符串生成方法获取随机字符串
        $data['appid'] = self::appid;
        $data['mch_id'] = self::mch_id;
        $data['nonce_str'] = $nonce_str;
        $data['spbill_create_ip'] = self::get_client_ip();
        $data['notify_url'] = self::notify_url;
        $data['trade_type'] = self::trade_type;      //将参与签名的数据保存到数组
//        var_dump($data);die;
        // 注意：以上几个参数是追加到$data中的，$data中应该同时包含开发文档中要求必填的剔除sign以外的所有数据
        $sign = self::getSign($data);        //获取签名
//        var_dump($sign);die;
        $data = sprintf($sTpl, self::appid, $data['body'], self::mch_id, $nonce_str, self::notify_url, $data['out_trade_no'], $data['spbill_create_ip'], $data['total_fee'], self::trade_type, $sign);
//        var_dump($data);die;
        //生成xml数据格式
        return $data;
    }
    //获取支付参数
    static public function sendRequest($data=array())
    {
        $post_data = self::setSendData($data);            //获取要发送的数据

        $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        $content = self::http_post($url, $post_data);

        // $objectxml = (array)simplexml_load_string($content,'SimpleXMLElement',LIBXML_NOCDATA); //将微信返回的XML 转换成数组
        $objectxml = self::xmlToObject($content);
//        var_dump($objectxml);die;
        return $objectxml;      //返回请求到的数据

    }
    public function setNotifyUrl($url) {
        if (is_string($url)) {
            $this->notify_url = $url;
        }
    }

    /**
     * 获取客户端支付信息
     * @param  Array $data 参与签名的信息数组
     * @return String       签名字符串
     */
     static public function getClientPay($data) {
        $sign = self::getSign($data);        // 生成签名并返回
        return $sign;
    }
    //生成第二次签名，返回给客户端
     static public function secondRequest($firstObj){

        if ($firstObj->return_code == 'FAIL') {
            return $firstObj->return_msg;            // 如果微信返回错误码为FAIL，则代表请求失败，返回失败信息；
        } else {
            //如果上一次请求成功，那么我们将返回的数据重新拼装，进行第二次签名
            $resignData = array(
                'appid'     =>  $firstObj->appid,
                'partnerid' =>	$firstObj->mch_id,
                'prepayid'  =>  $firstObj->prepay_id,
                'noncestr'  =>  $firstObj->nonce_str,
                'timestamp' =>  time(),
                'package'   =>  'Sign=WXPay'
            );

            //二次签名；
            $sign = self::getClientPay($resignData);

            $resignData['sign'] = $sign;

            return $resignData;
        }
    }
    //xml格式转object
    static public function xmlToObject($xmlStr) {
        if (!is_string($xmlStr) || empty($xmlStr)) {
            return false;
        }
        // 由于解析xml的时候，即使被解析的变量为空，依然不会报错，会返回一个空的对象，所以，我们这里做了处理，当被解析的变量不是字符串，或者该变量为空，直接返回false
        $postObj = simplexml_load_string($xmlStr, 'SimpleXMLElement', LIBXML_NOCDATA);
        $postObj = json_decode(json_encode($postObj));
        //将xml数据转换成对象返回
        return $postObj;
    }

    //获取回调参数
    public function getNotifyData() {
        $postXml = $GLOBALS["HTTP_RAW_POST_DATA"];    // 接受通知参数；
        if (empty($postXml)) {
            return false;
        }
        $postObj = self::xmlToObject($postXml);      // 调用解析方法，将xml数据解析成对象
        if ($postObj === false) {
            return false;
        }
        if (!empty($postObj->return_code)) {
            if ($postObj->return_code == 'FAIL') {
                return false;
            }
        }
        return $postObj;          // 返回结果对象；
    }


    /**
     * 查询订单状态
     * @param  Curl   $curl         工具类
     * @param  string $out_trade_no 订单号
     * @return xml               订单查询结果
     */
    public function queryOrder($out_trade_no) {
        $nonce_str = self::getNonceStr();
        $data = array(
            'appid'        => self::appid,
            'mch_id'       => self::mch_id,
            'out_trade_no' => $out_trade_no,
            'nonce_str'    => $nonce_str,
        );
        $sign = self::getSign($data);
        $xml_data = '<xml>
	                   <appid>%s</appid>
	                   <mch_id>%s</mch_id>
	                   <nonce_str>%s</nonce_str>
	                   <out_trade_no>%s</out_trade_no>
	                   <sign>%s</sign>
	                </xml>';
        $xml_data = sprintf($xml_data,self::appid,self::mch_id,$nonce_str,$out_trade_no,$sign);
        $url = "https://api.mch.weixin.qq.com/pay/orderquery";
        $content = self::http_post($url,$xml_data);
        return $content;
    }
    static public function payment($body,$order_number,$total_price){
        //为客户端支付准备
        $data = array(
            "body"=> $body,  //商品信息
            "total_fee"=> $total_price*100,   //支付价格
//            "total_fee"=> 0.01*100,   //支付价格
            "out_trade_no"=>$order_number,  //自己的订单id，支付成功回调修改订单状态
        );
        //第一次加密
        $objectxml         = self::sendRequest($data);
        //第二次加密
        $wxPayArr          = self::secondRequest($objectxml);
        //返回给客户端的支付参数，这里我转成了用&拼接，和支付宝一样的，微信默认是数组
//        return self::arrToString($wxPayArr);
        return $wxPayArr;

    }
}
